<template>
    <master :title="title">
      <listpage ref="listpage"></listpage>
      <modal :confirm-box-style="contentStyle" title="新增内容" :visible="show">
        这是一个自定义内容弹出框
      </modal>
  
      <!-- vis关系图基础展示 -->
  
      <el-dialog title="知识图谱" :visible.sync="dialogVisible" :before-close="destroyNetwork" :fullscreen="true">
        <span>
          <!-- vis关系图基础展示 -->
          <div>
            <!--width,height 画布的宽度，高度。 可以是百分比或像素，一般在dom元素上设置 -->
            <div id="network_id1" class="network" style="width:100%; height:80vh;border:1px solid #939393;"></div>
          </div>
        </span>
        <span slot="footer" class="dialog-footer">
          <el-button type="success" @click="Doresult">总 结</el-button>
          <el-button type="primary" @click="destroyNetwork">确 定</el-button>
        </span>
      </el-dialog>
  
      <el-dialog title="课程总结" :visible.sync="Doresultshow" width="50%">
        <!-- <span>
          {{ this.Doresultdata }}
          &nbsp;&nbsp;&nbsp;&nbsp;通过对本课程学生学习情况的综合评估，发现以下总体特点与问题。<br>
          &nbsp;&nbsp;&nbsp;&nbsp;大部分学生展现出对课程的浓厚兴趣与积极态度，课堂上能够主动参与讨论，
          对基础概念如地图投影、空间数据模型等有一定理解。实践环节中，部分学生能够运用所学知识完成简单的 Web
          地图发布与基本功能开发，表现出较强的动手能力与知识迁移能力。<br>
          &nbsp;&nbsp;&nbsp;&nbsp;然而，学习过程中也暴露出一些问题。理论学习方面，部分学生对复杂算法与高级数据处理技术理解不够深入，
          如空间分析算法原理仅停留在表面认知，在实际应用场景分析时难以灵活运用。实践操作上，遇到综合性项目时，
          学生的协作能力与系统架构设计能力不足，导致项目进度滞后或功能不完善。<br>
          &nbsp;&nbsp;&nbsp;&nbsp;针对这些问题，有以下改进建议。教学内容上，可增加前沿案例分析，深度剖析复杂理论知识与实际应用的结合点，
          帮助学生加深理解。教学方法应更加多元化，引入项目驱动教学，以小组形式开展复杂项目实践，培养学生团队协作与系统设计能力。<br>
          &nbsp;&nbsp;&nbsp;&nbsp;同时，加强实践指导环节，针对学生实践中遇到的共性问题及时答疑解惑，促进知识吸收与技能提升。此外，还可鼓励学生参与开源
          WebGis 项目开发，拓宽视野，积累经验，为未来从事相关领域工作奠定坚实基础。
        </span> -->
        <!-- <span>{{ this.Doresultdata }}</span> -->
        <span v-html="Doresultdata"></span>
  
        <span slot="footer" class="dialog-footer">
          <el-button @click="Doresultshow = false">取 消</el-button>
          <el-button type="primary" @click="Doresultshow = false">确 定</el-button>
        </span>
      </el-dialog>
  
    </master>
    <net-echart>
    </net-echart>
  
  </template>
  <script type="text/javascript">
    (function (G) {
      var _this = null;
      G.vue({
        "usingComponents": {///引入组件
          "master": "/components/Master/Master",///母版组件
          "listpage": "/components/List/Page/Page",///通用列表组件
          "modal": "/components/Modal/Modal",
          "net-echart": "/components/NetEchart/NetEchart",
        },
        "enablePullDownRefresh": true
      }, G.modelList({///配置listpage组件
        modelName: 'sp_course',///entity名称
        title: '课程列表',
        listPage(list, query) {///query为前一个页面传过来的参数
          let type = query.type
          // list.getUrl = '/api/model/v_course?join=user';///列表请求API
          list.getUrl = '/api/model/sp_course';///列表请求API
          if (query.type) list.getUrl += '&type=' + query.type
          list.searchKey = 'name'///搜索字段，来源于列表数据模型
          // list.models.createTime = {
          //   label: '发布时间',
          //   unList: true,
          //   type: 'datetime'
          // }
  
          ///list.canCheck = false ///取消选中按钮 同时取消批量删除
          ///list.canBack = true ///添加返回上一页按钮
          ///list.actions.add = false ///取消新增按钮
          ///list.actions.delet = undefined ///取消行删除按钮
          ///list.actions.edit = undefined ///取消行修改按钮
          // list.models = {/// 列表数据模型，默认使用后台返回对应的模型
          //   id:{
          //     label:'编号'
          //   }
          // }
          // list.addDataChange = function(item,i){///数据监听回掉
          //   if(item.isNew) {
          //     item.dis_push = true///此行去掉推送按钮
          //   }
          // }
          list.actions.detail1 = {///定义操作按钮，默认存在修改和删除
            name: '知识图谱',///按钮名称
            action(event) {///点击回调s
              console.log("传递的课程数据", event);
              // log("_this.dialogVisible", _this.dialogVisible);
              _this.dialogVisible = true;
              _this.getNetList(event.detail.id);
              _this.init();
            }
          },
            list.actions.detail2 = {///定义操作按钮，默认存在修改和删除
              name: '出题',///按钮名称
              action(event) {///点击回调
                // 定义需要传递的参数
                const SpCourseId = event.detail.id;
  
                // 构建目标页面的 URL 并携带参数
                //const targetUrl = `http://localhost:3001/#/pages/practice/index?SpCourseId=${SpCourseId}`;
                G.$go('/pages/practice/index?SpCourseId=' + SpCourseId)
  
                // 使用 window.location 跳转到目标页面并传递参数
                // window.location.href = targetUrl;
              }
            },
            list.actions.detail3 = {///定义操作按钮，默认存在修改和删除
              name: '总结',///按钮名称
              action(event) { // 点击回调
                console.log("传递的课程数据", event);
  
                // 构造请求
                fetch('http://192.168.1.109:8080/sp-course/createWord', {
                  method: 'POST', // 使用 POST 请求
                  headers: {
                    'Content-Type': 'application/json', // 指定发送 JSON 格式
                  },
                  body: JSON.stringify(event.detail), // 将对象转为 JSON 字符串
                })
                  .then(response => {
                    if (!response.ok) {
                      throw new Error(`HTTP error! status: ${response.status}`);
                    }
                    return response.blob(); // 获取文件数据
                  })
                  .then(blob => {
                    // 创建 Blob 对象的 URL
                    const url = window.URL.createObjectURL(blob);
  
                    // 创建一个 <a> 元素用于下载
                    const a = document.createElement('a');
                    a.href = url;
  
                    // 设置文件名（可以根据后端返回的文件名动态设置）
                    a.download = '课程数据.docx'; // 自定义文件名
                    document.body.appendChild(a);
  
                    // 触发下载
                    a.click();
  
                    // 移除临时的 <a> 元素
                    document.body.removeChild(a);
  
                    // 释放 Blob URL
                    window.URL.revokeObjectURL(url);
  
                    console.log('文件下载成功');
                  })
                  .catch(error => {
                    console.error('发送失败:', error);
                  });
              }
  
            }
        },
        modeleditquery(edit, event, query) {///编辑页面 edit对象，event事件，query前一个页面传过来的参数
          if (query.type) {
            edit.values.type = query.type
            edit.models.type.type = ''
          }
          // edit.models.createTime.type = ''
          // edit.models = {///编辑数据模型，默认使用后台返回的对应模型
          //   title:{
          //     label:'标题',
          //     type:'input'
          //   }
          // }
          ///edit.readOnly = true ///设置页面只读
          ///edit.meth = 'PUT' ///请求方式
          ///edit.url = '/api/model/article' ///请求地址
        }
      }, {
        data: {
          // 测试
          ssss: [],
          Doresultdata: "",
          contentStyle: '',
          show: false,
          Doresultshow: false,
          /* 11111111111111111111vis知识图谱1111111111111111111111 */
          nodes: [],
          edges: [],
          // network:null,
          container: null,
          //   节点数组
          nodesArray: [
            {
              id: 0,
              name: "小明",
              group: "1",
            },
            {
              id: 1,
              name: "大立",
              group: "1",
            },
            {
              id: 2,
              name: "珊珊",
              group: "1",
            },
            {
              id: 3,
              name: "旺旺",
              group: "animal",
            },
            {
              id: 4,
              name: "咕咕",
              group: "animal",
            },
          ],
          //   关系线数组
          edgesArray: [
            { id: "e1", from: 0, to: 3, label: "宠物" },
            { id: "e2", from: 1, to: 4, label: "宠物" },
            { id: "e3", from: 0, to: 1, label: "好朋友" },
            { id: "e4", from: 1, to: 2, label: "好朋友" },
          ],
  
          options: {},
          datas: {},
          dialogVisible: false,
          /* 11111111111111111111vis知识图谱1111111111111111111111 */
        },
        methods: {
          /* 11111111111111111111111111111111111vis知识图谱11111111111111111111111111111111111111111111 */
          destroyNetwork(done) {
            if (this.network) {
              this.dialogVisible = false;
              // console.log("销毁网络",this.network);
  
              this.network.destroy();
              // console.log("销毁网络",this.network);
  
              this.network = null;
              // console.log("销毁网络",this.network);
              console.log("销毁网络", done);
  
  
              if (done) {
                done(); // 必须调用，才能关闭对话框
              }
            }
          },
          // 初始化network
          init() {
            // console.log("初始化网络this.network",this.network);
  
            this.$nextTick(() => {
              // 使用 $nextTick 方法，它会在下次 DOM 更新循环结束之后执行回调函数，
              // 确保相关的 DOM 操作在元素渲染完成后执行，常用于依赖于 DOM 状态更新后的操作
              //1.创建一个nodes对象，用于存储网络拓扑图中的节点数据
              this.nodes = new vis.DataSet([]);
              //2.创建一个edges对象，用于存储网络拓扑图中的边（连线）数据
              this.edges = new vis.DataSet([]);
              // 3.创建一个网络拓扑图
              // 通过 document.getElementById 方法获取页面中 id 为 "network_id1" 的 DOM 元素，
              // 这个元素将作为网络拓扑图的容器
              this.container = document.getElementById("network_id1");
              // 4.创建datas对象
              // 用于后续初始化网络拓扑图时传递数据
              this.datas = {
                nodes: this.nodes,
                edges: this.edges,
              };
              // 5.全局配置
              this.options = {
                autoResize: true, //网络将自动检测其容器的大小调整，并相应地重绘自身，若为true，则会自动调整大小
                locale: "cn", //语言设置：工具栏显示中文
                //设置语言
                locales: {
                  cn: {
                    //工具栏中文翻译
                    edit: "编辑",
                    del: "删除当前节点或关系",
                    back: "返回",
                    addNode: "添加节点",
                    addEdge: "添加连线",
                    editNode: "编辑节点",
                    editEdge: "编辑连线",
                    addDescription: "点击空白处可添加节点",
                    edgeDescription: "点击某个节点拖拽连线可连接另一个节点",
                    editEdgeDescription: "可拖拽连线改变关系",
                    createEdgeError: "无法将边连接到集群",
                    deleteClusterError: "无法删除集群.",
                    editClusterError: "无法编辑群集'",
                  },
                },
                //该配置项主要用来生成一个可视化的配置器
                // configure 配置项相关，目前 enabled 设置为 false 表示不启用可视化配置器，
                // filter 规定了配置器可配置的对象（节点和边），container 指定配置器所在的容器（即网络拓扑图的容器），
                // showButton 为 true 表示显示相关操作按钮（不过目前由于 enabled 为 false 暂不生效）
                configure: {
                  enabled: false,
                  filter: "nodes,edges",
                  container: this.container,
                  showButton: true,
                },
                // 组模块
                groups: {
                  // useDefaultGroups 为 true 表示使用默认的分组设置（具体默认设置可能由库本身决定），
                  useDefaultGroups: true,
                  myGroupId: {},
                  1: {
                    shape: "circle",
                    color: { background: "#78e08f" },
                    font: { color: "white" },
                    // size: 300, // 统一设置大小
                    fixedSize: true, // 固定大小
                  },
                  // animal: {
                  //   shape: "dot",
                  // color: { background: "#7ed6df" },
                  // },
                  2: {
                    shape: "circle",
                    color: { background: "#69c4ff" },
                    font: { color: "white" },
                  },
                  3: {
                    shape: "dot",
                    color: { background: "#a7d3f5" },
                    // font: { color: "#ff8c94" },
                    font: { color: "#69c4ff" },
                    // font: { color: "#fff700" },
                    // font: { color: "#ff9100" },
                  },
                  4: {
                    shape: "dot",
                    color: { background: "#f9c3d3" },
                    // font: { color: "#ff8c94" },
                    // font: { color: "#d600ff" },
                    // font: { color: "#fff700" },
                    // font: { color: "#ff9100" },
                    font: { color: "#69c4ff" },
                  },
                  5: {
                    shape: "circle",
                    color: { background: "#ff8c94" },
                    font: { color: "white" },
                  },
                  6: {
                    shape: "circle",
                    color: { background: "#ff6b6b" },
                    font: { color: "white" },
                  }
                },
  
                // 设置节点样式
                nodes: {
                  shape: "ellipse", //节点的外观。为circle时label显示在节点内，为dot时label显示在节点下方
                  // size: 30,
                  // widthConstraint: false, // 移除宽度限制
                  widthConstraint: {
                    minimum: 100,
                    maximum: 100,
                  },
                  shadow: false, //如果为true，则节点使用默认设置投射阴影。
                  font: {
                    //字体配置
                    size: 20,
                    color: "rgb(117, 218, 167)",
                    align: "center",
                  },
                  color: {
                    border: "transparent", //节点边框颜色
                    background: "#fd91b7", //节点背景颜色
                    highlight: {
                      //节点选中时状态颜色
                      border: "rgb(117, 218, 167)",
                      background: "rgb(117, 218, 167)",
                    },
                    hover: {
                      //节点鼠标滑过时状态颜色
                      border: "#dff9fb",
                      background: "#88dab1",
                    },
                  },
                  margin: 5, //当形状设置为box、circle、database、icon、text；label的边距
                  // widthConstraint: 100, //设置数字，将节点的最小和最大宽度设为该值,当值设为很小的时候，label会换行，节点会保持一个最小值，里边的内容会换行
                  borderWidth: 1, //节点边框宽度，单位为px
                  borderWidthSelected: 3, //节点被选中时边框的宽度，单位为px
                  labelHighlightBold: false, //确定选择节点时标签是否变为粗体
                },
                // 边线配置
                // 对网络拓扑图中边（连线）的样式、长度、颜色、字体以及连线的平滑度、箭头等属性进行配置，
                // 例如控制连线宽度、不同交互状态下的颜色变化、是否显示阴影、连线是否平滑（曲线还是直线）以及箭头指向等
                edges: {
                  width: 1,
                  length: 200,// 原始的连线长度（后续会根据不同组间的设置覆盖）
                  color: {
                    color: "#848499",
                    highlight: "rgb(117, 218, 167)",
                    hover: "#88dab1",
                    inherit: "from",
                    opacity: 1.0,
                  },
                  font: {
                    color: "#343434",
                    size: 18, // px
                    face: "arial",
                    background: "none",
                    strokeWidth: 2, // px
                    strokeColor: "#ffffff",
                    align: "horizontal",
                    multi: false,
                    vadjust: 0,
                    bold: {
                      color: "#343434",
                      size: 14, // px
                      face: "arial",
                      vadjust: 0,
                      mod: "bold",
                    },
                    ital: {
                      color: "#343434",
                      size: 14, // px
                      face: "arial",
                      vadjust: 0,
                      mod: "italic",
                    },
                    boldital: {
                      color: "#343434",
                      size: 14, // px
                      face: "arial",
                      vadjust: 0,
                      mod: "bold italic",
                    },
                    mono: {
                      color: "#343434",
                      size: 15, // px
                      face: "courier new",
                      vadjust: 2,
                      mod: "",
                    },
                  },
                  shadow: false,
                  smooth: {
                    //设置两个节点之前的连线的状态
                    enabled: true, //默认是true，设置为false之后，两个节点之前的连线始终为直线，不会出现贝塞尔曲线
                  },
                  arrows: { to: true }, //箭头指向to
                },
                // 布局
                layout: {
                  randomSeed: 1, //配置每次生成的节点位置都一样，参数为数字1、2等
                  // hierarchical: {
                  //   direction: "DU", //UD:上下 DU:下上 LR:左右 RL:右左
                  //   sortMethod: "directed",
                  // }, //层级结构显示}
                },
                //物理引擎-计算节点之前斥力，进行自动排列的属性
                physics: {
                  enabled: true, //默认是true，设置为false后，节点将不会自动改变，拖动谁谁动。不影响其他的节点
                  // enabled属性用于控制物理引擎是否启用。当设置为true时，物理引擎生效，节点之间会基于相关物理规则（如斥力等）自动排列；
                  // 若设置为false，则节点失去自动排列的能力，只有手动拖动某个节点时，该节点才会移动，不会影响其他未被拖动的节点位置。
                  // solver: "forceAtlas2Based", // 使用更合适的布局算法
                  stabilization: { iterations: 200 }, // 增加迭代次数，使布局更加稳定
                  barnesHut: {
                    gravitationalConstant: -12000,
                    // 引力常量，用于控制节点之间的引力大小，值为负数表示是吸引力的作用方向，具体数值大小影响吸引力的强度，
                    // 该值与其他参数共同作用来决定节点在布局中的相对位置关系。
                    centralGravity: 0.3,
                    // 中心引力系数，决定了整体布局向中心聚集的趋势强度，取值范围通常在0到1之间，
                    // 该值越大，节点越倾向于向整个布局的中心位置聚集。
                    springLength: 120,
                    // 弹性长度，可理解为节点之间通过类似弹簧连接时的原始长度，当节点间距离偏离这个长度时，会基于弹簧系数产生相应的作用力，
                    // 用于调整节点之间的间距，使其在一定程度上保持相对合理的分布。
                    springConstant: 0.04,
                    // 弹簧系数，决定了节点之间类似弹簧连接时的弹性强度，即当节点间距偏离弹簧长度时，该系数决定了节点受到的拉力或推力大小，
                    // 与弹簧长度配合影响节点的动态排列效果。
                    damping: 0.09,
                    // 阻尼系数，用于模拟物理世界中的摩擦力等阻尼效果，控制节点在运动过程中的减速情况，避免节点无限制地来回摆动或运动，
                    // 使节点的运动能更自然、平稳地趋于稳定状态。
                    avoidOverlap: 0,
                    // 避免重叠系数（具体含义可能因库的实现有所差异），通常用于控制节点之间尽量不出现重叠的程度，
                    // 值越大越倾向于严格避免节点重叠，这里设置为0可能表示不进行相关的强力避免重叠处理或采用默认的较弱处理方式。
                  },
                },
                //用于所有用户与网络的交互。处理鼠标和触摸事件以及导航按钮和弹出窗口
                interaction: {
                  hover: true,
                  // 当鼠标悬停（hover）在节点或连线上时，触发相应的交互效果（比如改变样式等），这里设置为true表示启用该交互功能。
                  dragNodes: true, //是否能拖动节点
                  // 设置为true表示允许用户通过鼠标拖动节点来改变其在网络拓扑图中的位置，若为false则禁止拖动节点操作。
                  dragView: true, //是否能拖动画布
                  // 如果设置为true，用户可以通过鼠标拖动整个网络拓扑图的视图（画布）来进行平移查看不同区域，若为false则不能进行此操作。
                  hover: true, //鼠标移过后加粗该节点和连接线
                  // 再次出现的hover属性（这里可能存在代码重复或可优化的情况，但功能上同样是控制鼠标悬停交互效果），
                  // 设置为true表示当鼠标悬停在节点或连线上时，相应的节点和连接线会以加粗等方式突出显示，便于用户识别和查看。
                  multiselect: true, //按 ctrl 多选
                  // 设置为true后，用户可以通过按住Ctrl键（在常见的操作系统交互习惯下）来实现多选节点或其他元素的操作，方便批量处理。
                  selectable: true, //是否可以点击选择
                  // 若为true，则用户可以通过鼠标点击来选中节点等元素，选中后可进行后续相关的操作（比如查看属性、删除等），若为false则禁止点击选择操作。
                  selectConnectedEdges: true, //选择节点后是否显示连接线
                  // 当用户选中某个节点时，设置为true会自动显示与该节点相连接的所有连接线，便于查看节点之间的关联关系，若为false则不显示相关连接线。
                  hoverConnectedEdges: true, //鼠标滑动节点后是否显示连接线
                  // 当鼠标悬停在某个节点上时，设置为true会显示与该节点相关的连接线，方便用户直观了解节点的连接情况，若为false则不进行此显示。
                  hideEdgesOnDrag: true, //拖动视图时不会绘制边
                  // 在拖动画布（视图）的过程中，设置为true表示暂时隐藏连接线，这样可以减少拖动过程中的视觉干扰，使视图拖动更流畅，
                  // 拖动结束后连接线会恢复显示，若为false则拖动时连接线依然正常显示。
                  zoomView: true, //是否能缩放画布
                  // 设置为true表示允许用户通过鼠标滚轮等方式对整个网络拓扑图的视图进行缩放操作，方便查看不同比例下的布局情况，若为false则禁止缩放操作。
                },
              };
  
              // 6.初始化网络拓扑图
              this.network = new vis.Network(this.container, this.datas, this.options);
  
              this.network.moveTo({ scale: 0.3 });
            });
  
          },
          //扩展节点 增加nodes和edges集合参数
          addNetworkParams(param) {
            // let colorList = ["#78e08f", "#69c4ff", "#a7d3f5", "#f9c3d3", "#ff8c94", "#ff6b6b"]
            // 添加节点
            for (var i = 0; i < param.nodes.length; i++) {
              var node = param.nodes[i];
  
              let color = "#ccc"; // 默认颜色
  
              if (node.score == 0) {
                color = "#ccc";
              } else if (node.score > 0 && node.score <= 100) {
                // 根据分值动态生成绿色，分值越高绿色越深    红和蓝小于绿色，绿色大于60保证是绿色
                let greenValue = Math.floor(255 - (node.score - 60) * 4.75); // 分值范围映射到绿色分量(120到225)
                greenValue = Math.max(140, Math.min(greenValue, 225)); // 限制范围在140-225之间
  
                // 红色和蓝色分量设置为绿色分量的一半，确保颜色显绿色
                let redBlueValue = Math.floor(greenValue * 0.5);
  
                color = `rgb(${redBlueValue}, ${greenValue}, ${redBlueValue})`; // 动态绿色
              } else {
                color = "#ff6b6b"; // 超过100分的颜色
              }
  
              this.nodes.add({
                label: node.name,
                size: node.size,
                color: color,
                ...node,
              });
            }
  
  
            //添加关系
            for (var i = 0; i < param.edges.length; i++) {
              var edge = param.edges[i];
              this.edges.add({
                length: 200,
                ...edge,
              });
            }
          },
          //获取数据
          async getNetList(spcourseId) {
            // const res = await G.get('/v1/students/chapter', { studentId: '1313554697507831808', courseId: '1313921323726012416' })
            // console.log("res=", res);
            const res = await G.get('{JAVAURL}/sp-chapter/list2', { courseId: spcourseId || '1313921323726012416' })
            // res的结构[SpChapter,{},{}……]
            // SpChapter(id=1, name=数据结构, type=1, major=null, introduce=null, classHour=1, parentId=0, spCourseId=2, remark=null, status=1, createTime=null, createBy=null, createTimeString=null, updateTime=null, updateTimeString=null, state=1, updateBy=null, unitId=cqcetSpark)
            if (Array.isArray(res.data)) {
              let chapter = res.data;
              //学生得分
              // 这里要求每个知识点学生的平均分
              let studentGrade = await this.NetworkgetstudentGrades(spcourseId);
              // 这里要求每个知识点的总分
              let chapterGrade = await this.NetworkchapterGrade(spcourseId);
              if (studentGrade.data && chapterGrade.data) {
                function convertArrayToMap(dataArray) {
                  const resultMap = {};
                  dataArray.forEach(item => {
                    resultMap[item.spChapterId] = { grade: parseFloat(item.grade) || 0 }; // 确保 grade 是数字类型
                  });
                  return resultMap;
                }
                let studentGradesMap = convertArrayToMap(studentGrade.data);
                let chapterGradesMap = convertArrayToMap(chapterGrade.data);
                let chapterObj = {}//知识点对象
                for (let i = 0; i < chapter.length; i++) {
                  // 1、
                  chapter[i].index = i;           // 为每个章节节点添加索引（顺序号）
                  chapter[i].children = [];       // 初始化每个节点的 children 数组（用于存储子节点）
                  chapter[i].totalGrade = chapterGradesMap[chapter[i].id]
                    && chapterGradesMap[chapter[i].id].grade ? chapterGradesMap[chapter[i].id].grade : 0
                  chapter[i].grade = studentGradesMap[chapter[i].id]
                    && studentGradesMap[chapter[i].id].grade ? studentGradesMap[chapter[i].id].grade : 0
                }
                // 1. 构建节点哈希表
                const nodeMap = {};
                chapter.forEach(node => {
                  nodeMap[node.id] = { ...node, children: [], processed: false }; // 初始化
                });
                // 2. 构建父子关系
                chapter.forEach(node => {
                  if (node.parentId && nodeMap[node.parentId]) {
                    nodeMap[node.parentId].children.push(nodeMap[node.id]);
                  }
                });
                // 3. 递归函数：累加叶子节点的分数到父节点
                function updateParentGrades(node) {
                  // 如果节点是叶子节点，直接返回当前的 totalGrade 和 grade
                  if (!node.children.length) {
                    return { totalGrade: node.totalGrade, grade: node.grade };
                  }
                  // 累加所有子节点的分数
                  node.children.forEach(child => {
                    const childGrades = updateParentGrades(child); // 递归更新子节
                    // 只有当子节点的 grade 不为 0 时，才进行累加
                    if (childGrades.grade !== 0) {
                      node.totalGrade += childGrades.totalGrade;
                      node.grade += childGrades.grade;
                    }
                  });
                  // 返回当前节点的总分，继续向上传递
                  return { totalGrade: node.totalGrade, grade: node.grade };
                }
                // 4. 查找根节点并更新分数
                chapter.forEach(node => {
                  if (!nodeMap[node.parentId]) {
                    updateParentGrades(nodeMap[node.id]); // 从根节点开始递归
                  }
                });
                chapter = Object.values(nodeMap);
                //计算百分比
                for (let i = 0; i < chapter.length; i++) {
                  chapter[i].score = !chapter[i].totalGrade ? 0 : parseInt(chapter[i].grade / chapter[i].totalGrade * 100)
                }


                // 使用 forEach 遍历并将结果推入 ssss 数组
                chapter.forEach(item => {
                  this.ssss.push({
                    id: item.id,
                    name: item.name,
                    grade: item.grade,
                    totalGrade: item.totalGrade
                  });
                });
                // console.log("ssss", this.ssss);
  
  
  
              }
              /* 11111111111111111111111111111111111反向点亮知识图谱代码11111111111111111111111111111111111111111111 */
              res.data = chapter
              // console.log("res.data反向点亮知识图谱版2", res.data);
  
              // 清空数组，避免重复添加
              this.nodesArray = [];
              this.edgesArray = [];
              // 遍历数据生成节点数组
              res.data.forEach(item => {
                //去掉5级和6级的子节点
                if (item.type != '5' && item.type != '6') {
                  this.nodesArray.push({
                    id: item.id,
                    label: item.name,
                    group: item.type,
                    size: item.type == '1' ? 100 :
                      item.type == '2' ? 100 :
                        item.type == '3' ? 40 :
                          item.type == '4' ? 40 :
                            item.type == '5' ? 40 : 30, // 默认值    
                    score: item.score
                  });
                }
  
              });
  
              // console.log("每个节点数据列表nodesArray=", this.nodesArray);
  
              // 遍历数据生成关系数组
              res.data.forEach(item => {
                //去掉5级和6级的子节点
                if (item.type != '5' && item.type != '6') {
                  if (item.parentId) { // 确保 parentId 存在
                    this.edgesArray.push({
                      from: item.id,
                      to: item.parentId,
                      // label: item.type // 可选
                    });
                  }
  
                }
  
              });
              // console.log("关系列表edgesArray=", this.edgesArray);
  
              var param = { nodes: this.nodesArray, edges: this.edgesArray };
              // console.log("param.nodes=", param.nodes);
  
              this.addNetworkParams(param);
  
            } else {
              console.error("列表没数据", res.data);
            }
  
          },
  
          // 获取学生成绩数据
          async NetworkgetstudentGrades(spcourseId) {
            let studentGrades = await G.get('{JAVAURL}/sp-chapter/getstudentGrade', { studentId: '1313554697507831808', courseId: spcourseId || '1313921323726012416' })
            console.log("学生平均成绩studentGrades=", studentGrades);
            return studentGrades
          },
          // 获取知识点总成绩
          async NetworkchapterGrade(spcourseId) {
            let chapterGrade = await G.get('{JAVAURL}/sp-chapter/chapterGrade', { courseId: spcourseId || '1313921323726012416' })
            console.log("知识点总成绩chapterGrade=", chapterGrade);
            return chapterGrade
          },
          /* 111111111111111111111111111111111111vis知识图谱1111111111111111111111111111111111111111111 */
  
          // 显示总结
          Doresult() {
            // 这里显示报错没定义Doresultshow但是不影响使用
  
            const Doresultdata1 = `根据考核学生考试成绩的结果:${JSON.stringify(this.ssss)}，简要分析树结构中这门课程中所有学生的成绩(grade/totalgrade)分布情况，学生掌握情况及其主要原因，对这门课程的学习情况进行总结，通过学习这门课程让学生学习和收获到了什么东西，并对各分段的同学提出教学改进意见与措施，我只需要分析结果，返回的格式带有能够被v-html渲染出来的样式`;
            // const encodedQuestion = encodeURIComponent(Doresultdata1);  // URL encode the parameter
            const ResultForm = {
              question: Doresultdata1
            };
            // console.log("ResultForm", ResultForm.question);
  
            // G.get(`{JAVAURL}/spark/question1?question=${encodedQuestion}`)
            G.post('{JAVAURL}/spark/question1', ResultForm)
              .then((res) => {
                console.log("res", res);
  
                this.Doresultdata = res.message;
                this.Doresultshow = true;
  
              })
              .catch(() => {
                this.$message({
                  type: 'error',
                  message: '请选择课程或知识点'
                });
              });
  
  
          }
  
        },
        mounted: function (query) {
          _this = this
  
        }
      }));
    })(Y)
  </script>
  <style scoped>
  
  </style>